﻿@using SubstrateHelper.Models;
@using SubstrateHelper.Data;
@using BootstrapBlazor.Components;
@using System;
@using System.Collections.Concurrent;
@using System.Collections.Generic;
@using System.Linq;
@using System.Threading.Tasks;
@using System.Timers;
@using Microsoft.EntityFrameworkCore;
@using System.IO;
@using OfficeOpenXml;

@inject SubstrateHelperDbContext Db
@inject MessageService MessageService
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject UserManager<IdentityUser> UserManager
@inject NavigationManager NavigationManager

<div class="container-fluid">
    <div class="row">
        <div class="col">
            <Table TItem="SubstrateInfo" @ref="TheTable"
                   IsPagination="true" PageItemsSource="@PageItemsSource"
                   IsStriped="true" IsBordered="true"
                   ShowToolbar="false" IsMultipleSelect="true" ShowExtendButtons="false"
                   Height="430"
                   style="width:1550px"
                   OnQueryAsync="@OnQueryAsync">

                <TableColumns>
                    <TableColumn @bind-Field="@context.LotNo" Width="100" Sortable="true" Fixed="true" />
                    <TableColumn @bind-Field="@context.PartNo" Width="70" Sortable="true" Fixed="true" />
                    <TableColumn @bind-Field="@context.SubstrateType" Width="100" Sortable="true" Fixed="true" />
                    <TableColumn @bind-Field="@context.Thickness" Width="70" FormatString="0.000" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.CPA" Width="80" FormatString="0.0" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.K_avg" Width="70" FormatString="0" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.ErrorC" Width="100" FormatString="0.000" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.DF_max" Width="70" FormatString="0" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.BDV" Width="70" FormatString="0" Sortable="true">

                    </TableColumn>

                    <TableColumn @bind-Field="@context.DesignError" Width="100" FormatString="0.00" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.Mask" Width="100" Sortable="true" />
                    <TableColumn @bind-Field="@context.ScheduledLotNo" Width="100" Sortable="true" />
                    <TableColumn @bind-Field="@context.SchedulingDate" FormatString="yyyy-MM-dd" Width="100" Sortable="true" />
                    <TableColumn @bind-Field="@context.ScheduleMemo" Width="200" />

                    <TableColumn @bind-Field="@context.SubstrateMaterialType" Width="100" Sortable="true" />
                    <TableColumn @bind-Field="@context.PlatedType" Width="120" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.Memo" Width="100" />

                    <TableColumn @bind-Field="@context.CuttingL" Width="100" FormatString="0.00" Sortable="true">

                    </TableColumn>
                    <TableColumn @bind-Field="@context.CuttingW" Width="100" FormatString="0.00" Sortable="true">

                    </TableColumn>


                    <TableColumn @bind-Field="@context.UploadingDate" FormatString="yyyy-MM-dd" Width="100" Sortable="true" />
                    <TableColumn @bind-Field="@context.Scheduler" Width="100" />
                    <TableColumn @bind-Field="@context.NakedLotNo" Width="130" Sortable="true" />

                    <TableColumn @bind-Field="@context.SchedulingType" Width="100" Sortable="true" />
                    @*<TableColumn @bind-Field="@context.OutputToNC" Width="100" Sortable="true" Fixed="true">
                        <Template Context="value">
                            <Checkbox Value="@value.Value" IsDisabled="true"></Checkbox>
                        </Template>
                    </TableColumn>*@

                </TableColumns>
            </Table>

        </div>
        <div class="col-1"></div>
    </div>
    <hr class="bg-primary alig" style="height: 2px;" />



    <div class="container-fluid">
        <div class="form-inline col-12 " style="width:2200px;">
            <div class="d-inline col-1">排产辅助录入：</div>
            <div class="form-inline col-2">
                <div class="d-inline">电容器规格：</div>
                <div class="d-inline col">
                    <Select @bind-Value="SelectedCapacitorType" Items="CapacitorTypes" ShowSearch="true" TValue="string" />
                </div>

            </div>
            <div class="form-inline col-2">
                <div class="d-inline">掩膜版：</div>
                <div class="d-inline col">
                    <Select @bind-Value="SelectedCapacitorMask" Items="CapacitorMasks" ShowSearch="true" TValue="string" />
                </div>

            </div>
            <div class="form-inline col-2">
                <div class="d-inline">排产单模板：</div>
                <select @bind="SelectedTemplate">
                    @for (int i = 0; i < SchedulingTemplates.Length; i++)
                    {
                        <option value="@i">@(Path.GetFileNameWithoutExtension(SchedulingTemplates[i]))</option>
                    }

                </select>
            </div>
            <div class="form-inline col-2">
                <div class="d-inline">交货日期：</div>
                <div class="d-inline col">
                    <input type="date" @bind="DiliveryDate">
                </div>

            </div>


        </div>
        <div class="form-inline col-12 mt-3">
            <span class="text-primary col-1">实际排产信息：</span>
            <div class="row col-11">
                <span>
                    <span class="text-secondary">电容器规格:</span>
                    <input type="text" @bind="SchedulingType" class="ml-2" style="width:291px" />
                </span>
                <span class="ml-3">
                    <span class="text-secondary">容量中心值(pF):</span>
                    @if (FixdCValue)
                    {
                        <input type="number" @bind="C_Value" class="number_input" disabled />
                    }
                    else
                    {
                        <input type="number" @bind="C_Value" class="number_input" step="0.1" />
                    }

                </span>
                <span class="ml-3">
                    <span class="text-secondary">掩膜版:</span>
                    @if (UseMask)
                    {
                        <input type="text" @bind="SchedulingMask" class="number_input" disabled />
                    }
                    else
                    {
                        <input type="text" @bind="SchedulingMask" class="number_input" />
                    }


                </span>

                <span class="ml-3">
                    <span class="text-secondary">切割长度(mm):</span>
                    @if (UseMask)
                    {
                        <input type="number" @bind="L_Value" class="number_input" disabled />
                    }
                    else
                    {
                        <input type="number" @bind="L_Value" class="number_input" step="0.001" />
                    }
                </span>
                <span class="ml-3">
                    <span class="text-secondary">切割宽度(mm):</span>
                    @if (UseMask)
                    {
                        <input type="number" @bind="W_Value" class="number_input" disabled />
                    }
                    else
                    {
                        <input type="number" @bind="W_Value" class="number_input" step="0.001" />
                    }

                </span>

                <span class="ml-3">
                    <span class="text-secondary">仿真系数:</span>
                    <input type="number" @bind="Sim_Value" class="number_input" disabled />
                </span>
            </div>
        </div>

    </div>



    <div class="form-inline col-12 mt-5">
        <span class="text-success">比容中心值(pF/mm^2):<input type="number" @bind="CPA_Value" class="text-success ml-2" style="width:150px" step="0.1" /></span>
        <Button class="ml-3" IsOutline="false" Color="Color.Secondary" @onclick="DoCalculateError">计算设计误差</Button>

        <span class="ml-5">
            <span class="text-secondary">已选片数:</span>
            <input type="number" @bind="SelectedCount" class="number_input" disabled />
        </span>

        <span class="ml-3">
            <span class="text-secondary">排产批号:</span>
            <input type="text" @bind="SchedulingLotNo" class="number_input" style="width:120px" />
        </span>

        <span class="ml-3">
            <span class="text-secondary">备注:</span>
            <input type="text" @bind="SchedulingMemo" class="number_input" style="width:120px" />
        </span>
        <span class="ml-3">
            <span class="text-secondary">特殊工艺要求:</span>
            <input type="text" @bind="SchedulingSpecialProcess" class="number_input" style="width:150px" />
        </span>

        <Button class="ml-3" IsOutline="false" Color="Color.Primary" @onclick="DoSchedule">排 产</Button>
        <Button class="ml-3" IsOutline="false" Color="Color.Danger" @onclick="DoDiscard">报 废</Button>
        <Button class="ml-3" IsOutline="false" Color="Color.Warning" @onclick="UndoSchedule">排产回退</Button>
        @*<Button class="ml-3" IsOutline="false" Color="Color.Success" @onclick="OutputToNC">NC入库</Button>*@
    </div>



</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />

@code {



    [Parameter]
    public List<SubstrateInfo> Items
    {
        get
        {
            return _Items;
        }
        set
        {
            _Items = value;
            if (TheTable != null)
            {
                TheTable.QueryAsync();
            }

        }
    }
    List<SubstrateInfo> _Items;

    private Table<SubstrateInfo> TheTable;
    protected IEnumerable<int> PageItemsSource => new int[] { 50, 100, 200 };

    List<SelectedItem> CapacitorTypes
    {
        get
        {
            if (_CapacitorTypes == null)
            {
                try
                {
                    var types = Db.CandidateLists.Where(x => x.Category == "CapacitorType").Select(x => x.Candidate);
                    List<CapacitorInfo> infos = new List<CapacitorInfo>();
                    foreach (var type in types)
                    {
                        var info = __GetCapacitorInfo(type);
                        if (info != null)
                        {
                            infos.Add(info);
                        }

                    }

                    //排序优先顺序：容量-电压-尺寸-字母
                    var strs = infos.OrderBy(x => x.CapacitorType).OrderBy(x => x.L_Value).OrderBy(x => x.V_Value).OrderBy(x => x.C_Value).Select(x => x.CapacitorType).ToList();

                    _CapacitorTypes = new List<SelectedItem>();
                    _CapacitorTypes.Add(new SelectedItem("", "未录入规格"));
                    foreach (var str in strs)
                    {
                        _CapacitorTypes.Add(new SelectedItem(str, str));
                    }


                    _OnCapacitorTypeUpdated();
                }
                catch
                {

                }


            }
            return _CapacitorTypes;
        }
    }
    List<SelectedItem> _CapacitorTypes = null;

    string SelectedCapacitorType
    {
        get
        {
            return _SelectedCapacitorType;
        }
        set
        {
            _SelectedCapacitorType = value;
            _OnCapacitorTypeUpdated();//更新掩膜版列表
        }
    }
    string _SelectedCapacitorType = "";

    List<SelectedItem> CapacitorMasks { get; set; }
    string SelectedCapacitorMask
    {
        get
        {
            return _SelectedCapacitorMask;
        }
        set
        {
            _SelectedCapacitorMask = value;
            _OnMaskUpdated();
        }
    }
    string _SelectedCapacitorMask;

    string[] SchedulingTemplates
    {
        get
        {
            if (_SchedulingTemplates == null)
            {
                string path = Path.Combine(Directory.GetCurrentDirectory(), "SchedulingTemplates");
                _SchedulingTemplates = Directory.GetFiles(path, "*.xlsx");
            }
            return _SchedulingTemplates;

        }
    }
    string[] _SchedulingTemplates = null;
    int SelectedTemplate { get; set; }

    string SchedulingMask { get; set; }
    string SchedulingType { get; set; }
    string SchedulingLotNo { get; set; }
    string SchedulingMemo { get; set; }
    string SchedulingSpecialProcess { get; set; }
    DateTime DiliveryDate { get; set; }

    double CPA_Value { get; set; }//比容中心值
                                  //bool IsTypeB { get; set; } = true;//是否留边型
    bool UseMask { get; set; }
    bool FixdCValue { get; set; }
    bool ErrorCalculated { get; set; }
    int SelectedCount { get; set; }

    double Sim_Value
    {
        get
        {
            return _Sim_Value;
        }
        set
        {
            _Sim_Value = value;
            _UpdateCPA();
        }
    }
    double _Sim_Value = 1.0;

    double C_Value
    {
        get { return _C_Value; }
        set
        {
            _C_Value = value;
            _UpdateCPA();
        }
    }
    double _C_Value;

    double L_Value
    {
        get
        {
            return _L_Value;
        }
        set
        {
            _L_Value = value;
            _UpdateCPA();
        }
    }
    double _L_Value;

    double W_Value
    {
        get
        {
            return _W_Value;
        }
        set
        {
            _W_Value = value;
            _UpdateCPA();
        }
    }
    double _W_Value;



    protected Task<QueryData<SubstrateInfo>> OnQueryAsync(QueryPageOptions options)
    {

        if (Items == null)
        {
            return Task.FromResult(new QueryData<SubstrateInfo>());
        }

        IEnumerable<SubstrateInfo> items = Items;

        // 过滤
        var isFiltered = false;
        if (options.Filters.Any())
        {
            items = items.Where(options.Filters.GetFilterFunc<SubstrateInfo>());
            isFiltered = true;
        }

        // 排序
        var isSorted = false;
        if (!string.IsNullOrEmpty(options.SortName))
        {
            // 外部未进行排序，内部自动进行排序处理
            var invoker = SortLambdaCache.GetOrAdd(typeof(SubstrateInfo), key => LambdaExtensions.GetSortLambda<SubstrateInfo>().Compile());
            items = invoker(items, options.SortName, options.SortOrder);
            isSorted = true;
        }

        // 设置记录总数
        var total = items.Count();

        // 内存分页
        items = items.Skip((options.PageIndex - 1) * options.PageItems).Take(options.PageItems).ToList();

        return Task.FromResult(new QueryData<SubstrateInfo>()
        {
            Items = items,
            TotalCount = total,
            IsSorted = isSorted,
            IsFiltered = isFiltered
        });
    }

    System.Timers.Timer Timer;
    string user;
    protected override async Task OnInitializedAsync()
    {
        //
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var User = authState.User;

        user = UserManager.GetUserId(User);

        //交货期
        DiliveryDate = DateTime.Now.AddDays(7).Date;

        //批号前缀 HCYYMMXXX
        DateTime date = DateTime.Now;
        SchedulingLotNo = $"HC{date.Year.ToString().Substring(2)}{date.Month:00}";


        //定时器->自动更新用户选择片数
        Timer = new System.Timers.Timer(500);
        Timer.Elapsed += (o, e) =>
        {
            InvokeAsync(() =>
            {
                if (TheTable != null)
                {
                    var selected = TheTable.SelectedRows;
                    if (selected != null)
                    {
                        SelectedCount = selected.Count();

                    }
                    else
                    {
                        SelectedCount = 0;
                    }
                }
                else
                {
                    SelectedCount = 0;
                }
                StateHasChanged();
            });


        };
        Timer.Enabled = true;


    }

    private static readonly ConcurrentDictionary<Type, Func<IEnumerable<SubstrateInfo>, string, SortOrder, IEnumerable<SubstrateInfo>>> SortLambdaCache = new ConcurrentDictionary<Type, Func<IEnumerable<SubstrateInfo>, string, SortOrder, IEnumerable<SubstrateInfo>>>();
    protected SubstrateInfo SearchModel { get; set; } = new SubstrateInfo();



    void _UpdateCPA()
    {


        CPA_Value = C_Value / L_Value / W_Value / Sim_Value;

    }

    void _OnMaskUpdated()
    {
        //切割尺寸（是否可更改）
        if (string.IsNullOrWhiteSpace(SelectedCapacitorMask))//无掩膜板
        {
            UseMask = false;
            Sim_Value = 1.0;
            SchedulingMask = "无";
        }
        else//有掩膜板
        {
            UseMask = true;
            var mask = Db.CapacitorMasks.Where(x => x.Name == SelectedCapacitorMask).FirstOrDefault();
            if (mask != null)
            {
                SchedulingMask = mask.Name;
                Sim_Value = mask.SimRatio;
                L_Value = mask.DX;
                W_Value = mask.DY;
            }
            else
            {
                UseMask = false;
                Sim_Value = 1.0;
                SchedulingMask = "无";
            }

        }


    }

    void _OnCapacitorTypeUpdated()
    {
        if (string.IsNullOrWhiteSpace(SelectedCapacitorType))//不限电容器规格
        {

            //更新掩膜版列表
            var strs = Db.CapacitorMasks.OrderBy(x => x.DX * x.DY * x.SimRatio).Select(x => x.Name).ToList();

            CapacitorMasks = new List<SelectedItem>();
            CapacitorMasks.Add(new SelectedItem("", "无"));
            foreach (var str in strs)
            {
                CapacitorMasks.Add(new SelectedItem(str, str));
            }

            //掩膜版选定为无
            SelectedCapacitorMask = "";

            //实际排产电容器规格
            SchedulingType = "";

            FixdCValue = false;
        }
        else
        {

            if (CapacitorTypes != null && CapacitorTypes.Count > 0)
            {
                FixdCValue = true;
                string capacitorType = SelectedCapacitorType;
                SchedulingType = capacitorType;
                var capInfo = __GetCapacitorInfo(capacitorType);
                //更新容量
                C_Value = capInfo.C_Value;


                //更新尺寸
                L_Value = capInfo.L_Value;
                W_Value = capInfo.W_Value;

                try
                {


                    var strs = Db.CapacitorMasks
                        .Join(Db.CapacitorType_Masks, m => m.Name, tm => tm.MaskName, (m, tm) => new { m, tm })
                        .Where(r => r.tm.CapacitorType == capacitorType)
                        .Select(r => r.m)
                        .OrderBy(m => (m.DX * m.DY * m.SimRatio))
                        .Select(x => x.Name)
                        .ToList();

                    CapacitorMasks = new List<SelectedItem>();

                    if (strs.Count > 0)
                    {
                        CapacitorMasks.Add(new SelectedItem("", "无"));
                        foreach (var str in strs)
                        {
                            CapacitorMasks.Add(new SelectedItem(str, str));
                        }
                    }




                }
                catch
                {
                    //跳过
                }

                if (CapacitorMasks == null || CapacitorMasks.Count <= 0)
                {
                    var strs = Db.CapacitorMasks.OrderBy(x => x.DX * x.DY * x.SimRatio).Select(x => x.Name).ToList();
                    CapacitorMasks = new List<SelectedItem>();
                    CapacitorMasks.Add(new SelectedItem("", "无"));
                    foreach (var str in strs)
                    {
                        CapacitorMasks.Add(new SelectedItem(str, str));
                    }
                    SelectedCapacitorMask = "";
                }
                else if (CapacitorMasks.Count > 1)
                {
                    SelectedCapacitorMask = CapacitorMasks[1].Value;
                }
                else
                {
                    SelectedCapacitorMask = "";
                }

            }
        }
    }

    CapacitorInfo __GetCapacitorInfo(string typeName)
    {
        try
        {
            CapacitorInfo capacitorInfo = new CapacitorInfo();

            capacitorInfo.CapacitorType = typeName;
            string[] strs = typeName.Split(new char[] { '-', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);

            //尺寸
            string strDim = strs[1];
            switch (strDim)
            {
                case "10":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.178;
                    break;
                case "15":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.178;
                    break;
                case "20":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.508;
                    break;
                case "25":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.635;
                    break;
                case "30":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.762;
                    break;
                case "35":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 0.889;
                    break;
                case "40":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 1.016;
                    break;
                case "50":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 1.270;
                    break;
                case "70":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 1.780;
                    break;
                case "90":
                    capacitorInfo.L_Value = capacitorInfo.W_Value = 2.290;
                    break;
                case "0201":
                    capacitorInfo.L_Value = 25.4 * 0.02;
                    capacitorInfo.W_Value = 25.4 * 0.01;
                    break;
                case "0402":
                    capacitorInfo.L_Value = 25.4 * 0.04;
                    capacitorInfo.W_Value = 25.4 * 0.02;
                    break;
                case "0603":
                    capacitorInfo.L_Value = 25.4 * 0.06;
                    capacitorInfo.W_Value = 25.4 * 0.03;
                    break;
                case "0805":
                    capacitorInfo.L_Value = 25.4 * 0.08;
                    capacitorInfo.W_Value = 25.4 * 0.05;
                    break;
                default:
                    string[] strs_dim = strDim.Split(new char[] { 'x', 'X', '×', '*' }, StringSplitOptions.RemoveEmptyEntries);
                    double d1 = double.Parse(strs_dim[0]);
                    double d2 = double.Parse(strs_dim[1]);
                    capacitorInfo.L_Value = Math.Max(d1, d2);
                    capacitorInfo.W_Value = Math.Min(d1, d2);
                    break;
            }

            //电压
            string strV = strs[3];
            strV = strV.Substring(0, strV.Length - 1);
            capacitorInfo.V_Value = double.Parse(strV);

            //容量
            string strC = strs[4];
            strC = strC.Substring(0, 3).ToUpper();

            if (strC.Contains('R'))
            {
                strC = strC.Replace('R', '.');
                capacitorInfo.C_Value = double.Parse(strC);
            }
            else
            {
                double c_base = double.Parse(strC.Substring(0, 2));
                int c_10 = int.Parse(strC.Substring(2));

                capacitorInfo.C_Value = c_base;
                for (int i = 0; i < c_10; i++)
                {
                    capacitorInfo.C_Value *= 10;
                }
            }




            return capacitorInfo;
        }
        catch (Exception ex)
        {
            return null;
        }
    }

    void DoCalculateError()
    {
        ErrorCalculated = false;
        if (CPA_Value <= 0 || double.IsNaN(CPA_Value) || double.IsInfinity(CPA_Value))
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：比容中心值错误！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        if (Items == null || Items.Count <= 0)
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：记录数量为0！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        try
        {
            foreach (var item in Items)
            {
                item.CalculateDesignError(CPA_Value);
            }

            Items = Items.OrderBy(x => x.DesignError).ToList();

            if (TheTable != null)
            {
                TheTable.QueryAsync();//刷新
            }

            ErrorCalculated = true;

            MessageService?.Show(new MessageOption()
            {
                Content = "成功：已计算设计误差！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Success
            });
        }
        catch (Exception ex)
        {
            MessageService?.Show(new MessageOption()
            {
                Content = $"发生错误：{ex.Message}",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
        }
    }

    void DoSchedule()
    {

        //必须具有调度角色
        if (!_IsScheduler())
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：您登录的账号没有包括“调度员”角色！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        //必须要先计算设计误差
        if ((!ErrorCalculated) && (!SchedulingLotNo.StartsWith("报废")))
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：排产前请计算设计误差！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        //
        if (TheTable != null)
        {
            if (TheTable.SelectedRows == null || TheTable.SelectedRows.Count() <= 0)
            {
                MessageService?.Show(new MessageOption()
                {
                    Content = "错误：所选择基板数量为0！",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Danger
                });
                return;
            }
        }
        else
        {
            return;
        }

        //验证
        if (string.IsNullOrWhiteSpace(SchedulingType) && (!SchedulingLotNo.StartsWith("报废")))//排产型号
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：电容器规格不能为空！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }




        DateTime date = DateTime.Now;
        if (string.IsNullOrWhiteSpace(SchedulingLotNo) || SchedulingLotNo == $"HC{date.Year.ToString().Substring(2)}{date.Month:00}")//批号
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：批号不能为空！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }



        using (var trans = Db.Database.BeginTransaction())
        {
            try
            {
                var rows = TheTable.SelectedRows;

                List<SubstrateInfo> substratesToSchedule = new List<SubstrateInfo>();

                double minThickness = double.MaxValue;
                double maxThickness = double.MinValue;
                foreach (var row in rows)
                {
                    if (!string.IsNullOrEmpty(row.ScheduledLotNo))//已排产
                    {
                        throw new Exception($"批号={row.LotNo},片号={row.PartNo}的基板已排产！");
                    }

                    if (string.IsNullOrEmpty(row.Memo))
                    {
                        row.Memo = SchedulingMemo;
                    }
                    else
                    {
                        row.Memo += $",{SchedulingMemo}";
                    }
                    //加入排产列表
                    substratesToSchedule.Add(row);

                    //基板厚度
                    if (row.Thickness < minThickness)
                    {
                        minThickness = row.Thickness;
                    }
                    if (row.Thickness > maxThickness)
                    {
                        maxThickness = row.Thickness;
                    }

                    row.SchedulingType = SchedulingType;
                    row.ScheduledLotNo = SchedulingLotNo;
                    row.SchedulingDate = DateTime.Now.Date;
                    row.ScheduleMemo = SchedulingSpecialProcess;//特殊工艺要求
                    row.Mask = SchedulingMask;
                    row.CuttingL = L_Value;
                    row.CuttingW = W_Value;

                    row.Scheduler = user;

                    var record = Db.SubstrateInfos.Where(si => si.LotNo == row.LotNo && si.PartNo == row.PartNo).First();

                    //bool same = object.ReferenceEquals(record, row);

                    record.CopyFrom(row);


                }



                Db.SaveChanges();

                {
                    //生成排产单，存放到GeneratedFile文件夹



                    string file = Path.Combine(Directory.GetCurrentDirectory(), $"GeneratedFile\\{SchedulingLotNo}.xlsx");
                    string folder = Path.Combine(Directory.GetCurrentDirectory(), "GeneratedFile");
                    Directory.CreateDirectory(folder);//确保路径存在
                    string fileTemplate = SchedulingTemplates[SelectedTemplate];
                    if (File.Exists(file))
                    {
                        File.Delete(file);
                    }
                    FileInfo fileInfo = new FileInfo(file);
                    FileInfo templateFile = new FileInfo(fileTemplate);
                    ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
                    ExcelPackage excelPackage = new ExcelPackage(templateFile);
                    var sheet = excelPackage.Workbook.Worksheets[0];
                    var replacements = Helpers.ExcelFileReplacer.GetReplacements(SchedulingTemplates[SelectedTemplate]);
                    List<TemplateReplacement> rps;
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "排产型号", SchedulingType);
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "排产数量", TheTable.SelectedRows.Count().ToString());
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "排产日期", DateTime.Now.ToString("yyyy-MM-dd"));
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "交货日期", DiliveryDate.ToString("yyyy-MM-dd"));
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "生产批号", SchedulingLotNo);
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "基板型号", _GetSubstrateType(substratesToSchedule));

                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "裸基板批号", _GetNakedLotNos(substratesToSchedule));
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "镀金基板批号", _GetLotNos(substratesToSchedule));
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "基板厚度", minThickness == maxThickness ? $"{minThickness:0.000}" : $"{minThickness:0.000}-{maxThickness:0.000}");

                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "掩膜版", SchedulingMask);
                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "划片尺寸", $"{L_Value}×{W_Value}");

                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "排产人", user);

                    Helpers.ExcelFileReplacer.Replace(sheet, replacements, "特殊要求", SchedulingSpecialProcess);

                    excelPackage.SaveAs(fileInfo);


                }
                trans.Commit();




                MessageService?.Show(new MessageOption()
                {
                    Content = "已保存更改到数据库!",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Success
                });

                //转到下载链接
                NavigationManager.NavigateTo($"Download/{SchedulingLotNo}", true);

                //排产批号重置，以免批号输入错误
                SchedulingLotNo = $"HC{date.Year.ToString().Substring(2)}{date.Month:00}";



            }
            catch (Exception ex)
            {
                trans.Rollback();
                MessageService?.Show(new MessageOption()
                {
                    Content = $"发生错误，数据库更改已回滚：{ex.Message}",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Danger
                });
            }
        }

    }

    string _GetSubstrateType(List<SubstrateInfo> substrates)
    {
        if (substrates != null && substrates.Count > 0)
        {
            foreach (var s in substrates)
            {
                if (!string.IsNullOrEmpty(s.SubstrateMaterialType))
                {
                    if (s.SubstrateType != "未分类")
                    {
                        return $"{s.SubstrateMaterialType}-{s.SubstrateType}";
                    }
                    return $"{s.SubstrateMaterialType}";

                }
            }
        }
        return null;
    }

    string _GetLotNos(List<SubstrateInfo> substrates)
    {
        if (substrates != null && substrates.Count > 0)
        {
            string re = "";
            foreach (var s in substrates)
            {
                re += $"批号：{s.LotNo} 片号：{s.PartNo};\r\n";
            }
            return re;
        }
        return null;
    }
    string _GetNakedLotNos(List<SubstrateInfo> substrates)
    {
        if (substrates != null && substrates.Count > 0)
        {
            string re = "";
            foreach (var s in substrates)
            {
                re += $"{s.NakedLotNo};\r\n";
            }
            return re;
        }
        return null;
    }

    void DoDiscard()
    {
        //必须具有调度角色
        if (!_IsScheduler())
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：您登录的账号没有包括“调度员”角色！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        var date = DateTime.Now;
        SchedulingLotNo = $"报废{date.Year:0000}{date.Month:00}{date.Day:00}";
        DoSchedule();
    }

    void UndoSchedule()
    {
        //必须具有调度角色
        if (!_IsScheduler())
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：您登录的账号没有包括“调度员”角色！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        if (TheTable != null)
        {
            if (TheTable.SelectedRows == null || TheTable.SelectedRows.Count() <= 0)
            {
                MessageService?.Show(new MessageOption()
                {
                    Content = "错误：所选择基板数量为0！",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Danger
                });
                return;
            }
        }
        else
        {
            return;
        }

        //验证

        using (var trans = Db.Database.BeginTransaction())
        {
            try
            {
                var rows = TheTable.SelectedRows;
                foreach (var row in rows)
                {




                    row.ScheduledLotNo = null;
                    row.SchedulingDate = null;
                    row.Mask = null;
                    row.CuttingL = null;
                    row.CuttingW = null;

                    row.ScheduleMemo = null;
                    row.SchedulingType = null;

                    row.Scheduler = null;
                    row.DesignError = null;


                    var record = Db.SubstrateInfos.Where(si => si.LotNo == row.LotNo && si.PartNo == row.PartNo).First();



                    //bool same = object.ReferenceEquals(record, row);

                    record.CopyFrom(row);


                }

                Db.SaveChanges();
                trans.Commit();
                MessageService?.Show(new MessageOption()
                {
                    Content = "已保存更改到数据库!",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Success
                });
            }
            catch (Exception ex)
            {
                trans.Rollback();
                MessageService?.Show(new MessageOption()
                {
                    Content = $"发生错误，数据库更改已回滚：{ex.Message}",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Danger
                });
            }
        }
    }

    bool _IsScheduler()
    {
        return Helpers.UserHelper.HasRole(user, "调度员");

    }

    bool _IsCangGuanYuan()
    {
        return Helpers.UserHelper.HasRole(user, "仓管员");
    }

    void OutputToNC()
    {
        //必须具有仓管员角色
        if (!_IsCangGuanYuan())
        {
            MessageService?.Show(new MessageOption()
            {
                Content = "错误：您登录的账号没有包括“仓管员”角色！",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
            return;
        }

        if (TheTable != null)
        {
            if (TheTable.SelectedRows == null || TheTable.SelectedRows.Count() <= 0)
            {
                MessageService?.Show(new MessageOption()
                {
                    Content = "错误：所选择基板数量为0！",
                    Icon = "fa fa-info-circle",
                    ShowDismiss = true,
                    Color = Color.Danger
                });
                return;
            }
        }
        else
        {
            return;
        }

        //验证

        var re = Helpers.NCHelper.OutputToNC(TheTable.SelectedRows);
        if (string.IsNullOrEmpty(re))//成功
        {
            MessageService?.Show(new MessageOption()
            {
                Content = $"NC入库成功!",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Success
            });
        }
        else//出错
        {
            MessageService?.Show(new MessageOption()
            {
                Content = $"发生错误，数据库更改已回滚：{re}",
                Icon = "fa fa-info-circle",
                ShowDismiss = true,
                Color = Color.Danger
            });
        }


    }




}
